/*
 * Copyright 2011 OpenWAF.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.openwaf.client.ui.widget;

import com.openwaf.client.core.Client;
import com.openwaf.client.core.Timer;
import com.openwaf.client.dom.DivElement;
import com.openwaf.client.dom.Document;
import com.openwaf.client.dom.Element;
import com.openwaf.client.dom.IFrameElement;
import com.openwaf.client.dom.NativeEvent;
import com.openwaf.client.dom.Node;
import com.openwaf.client.dom.StyleElement;
import com.openwaf.client.event.dom.KeyUpEvent;
import com.openwaf.client.event.dom.KeyUpHandler;
import com.openwaf.client.event.dom.LoadEvent;
import com.openwaf.client.event.dom.LoadHandler;
import com.openwaf.client.event.dom.MouseUpEvent;
import com.openwaf.client.event.dom.MouseUpHandler;
import com.openwaf.client.ui.UIControl;
import com.openwaf.client.ui.annotation.ViewElement;
import com.openwaf.client.ui.annotation.ViewMethod;

public class RichText extends UIControl {

    @ViewElement
    private IFrameElement frame;    
    private String editHTML;
    private Object selectedRange;
    private Document editDoc;

    public RichText() {
        selectedRange = null;
        editHTML = "";
        frame.addLoadHandler(new LoadHandler() {
            public void onLoad(LoadEvent event) {
                makeItEditable();
            }
        });

    }

    public void removeFormatting() {
        this.execCommand("removeFormat", null);
    }

    public boolean isBold() {
        return this.queryCommandState("Bold");
    }

    public void toggleBold() {
        this.execCommand("Bold", "false");
    }

    public boolean isItalic() {
        return this.queryCommandState("Italic");
    }

    public void toggleItalic() {
        this.execCommand("Italic", "false");
    }

    public boolean isUnderlined() {
        return this.queryCommandState("Underline");
    }

    public void toggleUnderline() {
        this.execCommand("Underline", "false");
    }

    public boolean isStrikethrough() {
        return this.queryCommandState("Strikethrough");
    }

    public void toggleStrikethrough() {
        this.execCommand("Strikethrough", "false");
    }

    public boolean isSubscript() {
        return this.queryCommandState("Subscript");
    }

    public void toggleSubscript() {
        this.execCommand("Subscript", "false");
    }

    public boolean isSuperscript() {
        return this.queryCommandState("Superscript");
    }

    public void toggleSuperscript() {
        this.execCommand("Superscript", "false");
    }

    public void indent() {
        this.execCommand("Indent", null);
    }

    public void outdent() {
        this.execCommand("Outdent", null);
    }

    public void redo() {
        this.execCommand("Redo", "false");
    }

    public void undo() {
        this.execCommand("Undo", "false");
    }

    public void insertHTML(String aHtml) {
        this.execCommand("InsertHTML", aHtml);
    }

    public void createLink(String aUrl) {
        this.execCommand("CreateLink", aUrl);
    }

    public void removeLink() {
        this.execCommand("Unlink", "false");
    }

    public void insertHorizontalRule() {
        this.execCommand("InsertHorizontalRule", null);
    }

    public void insertImage(String aUrl) {
        this.execCommand("InsertImage", aUrl);
    }

    public void insertOrderedList() {
        this.execCommand("InsertOrderedList", null);
    }

    public void insertUnorderedList() {
        this.execCommand("InsertUnorderedList", null);
    }

    public void removeFormat() {
        this.execCommand("RemoveFormat", null);
    }

    public String getBackColor() {
        return this.queryCommandString("BackColor");
    }

    public void setBackColor(String aColor) {
        if (Client.FIREFOX || Client.OPERA) {
            this.execCommand("HiliteColor", aColor);
        } else {
            this.execCommand("BackColor", aColor);
        }
    }

    public String getForeColor() {
        return this.queryCommandString("ForeColor");
    }

    public void setForeColor(String aColor) {
        this.execCommand("ForeColor", aColor);
    }

    public String getFontName() {
        return this.queryCommandString("FontName");
    }

    public void setFontName(String aName) {
        this.execCommand("FontName", aName);
    }

    public String getFontSize() {
        return this.queryCommandString("FontSize");
    }

    public void setFontSize(String fontsize) {
        this.execCommand("FontSize", fontsize);
    }

    public void selectAll() {
        this.execCommand("SelectAll", null);
    }
    


    @ViewMethod
    public void setHTML(Element element){
        setHTML(element.getInnerHTML());
    }


    public void setHTML(String html){
        this.editHTML=html;
        if(editDoc!=null){
            setHTML(editDoc,html);
        }
    }
    private native void initDoc(Object e)/*-{
         try{
            var s=e.body.style;
            s.marginLeft=0;
            s.marginRight=0;
            s.marginTop=0;
            s.marginBottom=0;
         }catch(e){}
    }-*/;
    private native void setHTML(Object e,String html)/*- {
       try{            
            e.body.innerHTML=html;            
       }catch(e) {            
       }
    }-*/;

    public String getHTML() {
        if(editDoc!=null){
            return getHTML(editDoc);
        }
        return null;
    }
    private native String getHTML(Object e)/*-{
       return e.body.innerHTML;
    }-*/;
    public void justify(String dir) {
        String cmd = "JustifyLeft";
        if (dir.equals("left")) {
            cmd = "JustifyLeft";
        } else if (dir.equals("right")) {
            cmd = "JustifyRight";
        } else if (dir.equals("center")) {
            cmd = "JustifyCenter";
        } else if (dir.equals("full")) {
            cmd = "JustifyFull";
        }
        this.execCommand(cmd, dir);
    }
    
    private void makeItEditable() {
        try {
            editDoc = this.frame.getContentDocument();
            if(editDoc==null){
               (new Timer() {
                    @Override
                    public void run() {
                        makeItEditable();
                    }
                }).schedule(1000);
                return;
            }
            initDoc(editDoc);
            setHTML(editDoc,editHTML);
            if(Client.IE && (! Client.IE_9_OR_ABOVE )){
               addEventsIELegacy(editDoc);
            }
            enableEditing(editDoc);
        } catch (Exception e) {
        }
    }
    private final void onFrameKeyUp(Object e){
        saveSelection();
    }
    private final void onFrameMouseUp(Object e){
        saveSelection();
    }
    private final native void addEventsNonIELegacy(Object ed)/*-{
      var s=this;
      ed.addEventListener("keyup",function(e){s.@com.openwaf.client.ui.widget.RichText::onFrameKeyUp(Ljava/lang/Object)(e);},true);
      ed.addEventListener("mouseup",function(e){s.@com.openwaf.client.ui.widget.RichText::onFrameMouseUp(Ljava/lang/Object)(e);},true);
    }-*/;
    private final native void addEventsIELegacy(Object ed)/*-{
      var s=this;
      ed.attachEvent("onkeyup",function(e){s.@com.openwaf.client.ui.widget.RichText::onFrameKeyUp(Ljava/lang/Object)(e);});
      ed.attachEvent("onmouseup",function(e){s.@com.openwaf.client.ui.widget.RichText::onFrameMouseUp(Ljava/lang/Object)(e);});
    }-*/;
    private final native void enableEditing(Object ed)/*- {
        if(ed.designMode){
           ed.designMode='On';
        }else{
        }
    }-*/;

 
    private void saveSelection() {
        this.selectedRange = this.getSelectionRange();
    }

    private void restoreSelection() {
        this.setSelectionRange(this.selectedRange);
    }

    public void setFocus() {
        if(Client.OPERA){
            frame.focus();
        }else{
            if(editDoc!=null){
                setFocus(editDoc);
            }
        }
    }
    private native void setFocus(Object e)/*-{
      e.body.focus();
    }-*/;

    public void setText(String text) {
        if(editDoc!=null){
            setText(editDoc,text);
        }
    }
    private native void setText(Object e,String text)/*-{
        e.body.innerHTML=text;
    }-*/;

    public String getText() {
        if(editDoc!=null){
            return getText();
        }
        return null;
    }
    private native String getText(Object e)/*-{
        return e.body.textContent;
    }-*/;

    public boolean queryCommandState(String cmd) {
        if(editDoc==null) return false;
        if (Client.IE) {
            this.restoreSelection();
        } else {
            this.setFocus();
        }
        return queryCommandState(editDoc, cmd);
    }
    private native boolean queryCommandState(Object e,String cmd)/*-{
        return e.queryCommandState(cmd);
    }-*/;

    public String queryCommandString(String cmd) {
        if (Client.IE) {
            this.restoreSelection();
        } else {
            this.setFocus();
        }
        return queryCommandString(editDoc,cmd);
    }
     private native String queryCommandString(Object e,String cmd)/*-{
        return e.queryCommandValue(cmd);
    }-*/;

    public boolean queryCommandBoolean(String cmd) {
        if (Client.IE) {
            this.restoreSelection();
        } else {
            this.setFocus();
        }
        return queryCommandBoolean(editDoc, cmd);

    }
    private native boolean queryCommandBoolean(Object e,String cmd)/*-{
        return !!e.queryCommandValue(cmd);
    }-*/;


    public void execCommand(String cmd, String param) {
        if (Client.IE) {
            this.restoreSelection();
        } else {
            this.setFocus();
        }
        execCommand(editDoc, cmd, false, param);
    }
    private native void execCommand(Object e,String cmd,boolean v,String param)/*-{
        return e.execCommand(cmd,v,param);
    }-*/;
   
    public native Element getSelectedNode()/*- {
    try{
    var fwin=this.@com.openwaf.client.ui.widget.RichText::frame.contentWindow;
    var doc = fwin.document;
    var node,selection;
    if (fwin.getSelection) {
    selection = fwin.getSelection();
    node = selection.anchorNode;
    }
    if (!node && doc.selection) {
    selection = doc.selection
    var range = selection.getRangeAt ? selection.getRangeAt(0) : selection.createRange();
    node = range.commonAncestorContainer ? range.commonAncestorContainer : range.parentElement ? range.parentElement() : range.item(0);
    }
    if (node) {
    return (node.nodeName == "#text" ? node.parentNode : node);
    }
    }catch(e){
    WAF.logtext(e);
    }
    return null;
    } -*/;

    public native Object getSelectionRange()/*- {
    
    var fwin=this.@com.openwaf.client.ui.widget.RichText::frame.contentWindow;
    var doc = fwin.document;
    var sel = fwin.getSelection ? fwin.getSelection() : doc.selection;
    var range;

    if (sel) {
    if (sel.createRange) {
    range = sel.createRange();
    }
    else if (sel.getRangeAt) {
    range = sel.getRangeAt(0);
    }
    else if (sel.anchorNode && sel.focusNode && doc.createRange) {

    range = doc.createRange();
    range.setStart(sel.anchorNode,sel.anchorOffset);
    range.setEnd(sel.focusNode, sel.focusOffset);

    if (range.collapsed !== sel.isCollapsed) {
    range.setStart(sel.focusNode, sel.focusOffset);
    range.setEnd(sel.anchorNode, sel.anchorOffset);
    }
    }

    }
    return range;

    }-*/;

    public native void setSelectionRange(Object range) /*-{
    
    var fwin=this.@com.openwaf.client.ui.widget.RichText::frame.contentWindow;
    var doc = fwin.document;
    var sel = fwin.getSelection ? fwin.getSelection() : doc.selection;

    if (sel && range) {
    if (range.select) {
    range.select();
    } else if (sel.removeAllRanges && sel.addRange) {
    sel.removeAllRanges();
    sel.addRange(range);
    }
    }

    }-*/;
}
